package ch.tiktak.games.powersudoku.solver;

import ch.tiktak.games.powersudoku.InvalidIndexException;
import ch.tiktak.games.powersudoku.InvalidSudokuValueException;
import ch.tiktak.games.powersudoku.SudokuGrid;

import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;

public class BruteForceSudokuSolver extends SudokuSolver {

    public BruteForceSudokuSolver(final SudokuGrid grid) {
        super(grid);
    }

    @Override
    public SudokuGrid solve() throws SudokuSolverException {

        // NB: Logging is bad for performance

        long start = System.currentTimeMillis();

        SudokuGrid copyGrid = originalGrid.copy();

        /**
         * Algorithm works as following:
         * Brute force with possible values
         *
         * If everything has been tried and no solution has been found, throw an exception
         */

        java.util.List<Point> emptyPoints = getEmptySquarePoints(copyGrid);
        HashMap<Point, java.util.List<Integer>> triedValues = new HashMap<Point, java.util.List<Integer>>();
        boolean solutionFound = false;

        int i = 0;
        while (!solutionFound) {
            Point currentPoint = emptyPoints.get(i);
            //logger.debug("Point n° " + i + " {" + currentPoint.x + ";" + currentPoint.y + "}");
            int rowIndex = currentPoint.x;
            int columnIndex = currentPoint.y;
            int value;
            java.util.List<Integer> possibleValues = copyGrid.getPossibleValues(rowIndex, columnIndex);
            //logger.debug("   Possible values: " + possibleValues);
            if (possibleValues.isEmpty()) {
                //logger.debug("   no possible values left. Going back to " + (i - 1));
                try {
                    copyGrid.setValue(rowIndex, columnIndex, -1);
                } catch (InvalidIndexException e) {
                    e.printStackTrace();
                } catch (InvalidSudokuValueException e) {
                    e.printStackTrace();
                }
                if (i != 0) {
                    triedValues.remove(currentPoint);
                    i--;
                }
            } else {
                boolean mustContinue = false;
                int possibilitiesToTry = possibleValues.size();
                for (int possibility : possibleValues) {
                    possibilitiesToTry--;
                    if (!(triedValues.containsKey(currentPoint) && triedValues.get(currentPoint).contains(possibility))) {
                        value = possibility;
                        try {
                            //logger.debug("   Try value " + value);
                            copyGrid.setValue(rowIndex, columnIndex, value);
                            if (!triedValues.containsKey(currentPoint)) {
                                triedValues.put(currentPoint, new ArrayList<Integer>());
                            }
                            triedValues.get(currentPoint).add(value);
                        } catch (InvalidIndexException e) {
                            e.printStackTrace();
                        } catch (InvalidSudokuValueException e) {
                            e.printStackTrace();
                        }
                        break;
                    } else if (possibilitiesToTry == 0) {
                        //logger.debug("   possible values found but already tried. Going back to " + (i - 1));
                        try {
                            copyGrid.setValue(rowIndex, columnIndex, -1);
                        } catch (InvalidIndexException e) {
                            e.printStackTrace();
                        } catch (InvalidSudokuValueException e) {
                            e.printStackTrace();
                        }
                        if (i != 0) {
                            triedValues.remove(currentPoint);
                            i--;
                            mustContinue = true;
                        }
                    }
                }
                if (mustContinue) {

                } else if (i == (emptyPoints.size() - 1)) {
                    if (copyGrid.isValid()) {
                        solutionFound = true;
                        break;
                    } else {
                        logger.error("Grid is not valid.");
                        throw new SudokuSolverException("Grid is not valid.");
                    }
                } else if (i == 0 && triedValues.containsKey(emptyPoints.get(i + 1))) {
                    solutionFound = false;
                    break;
                } else {
                    i++;
                }
            }
        }


        if (!solutionFound)

        {
            logger.error("No solution could be found.");
            throw new SudokuSolverException("No solution could be found.");
        }

        long stop = System.currentTimeMillis();
        int timeInMillis = (int) (stop - start);
        logger.info("Solved Sudoku in " + timeInMillis + " ms");

        return copyGrid;
    }

}
